package com.mrx.xfriendship.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.mrx.commons.annotations.XLogService;
import com.mrx.xfriendship.dao.DashboardMapper;
import com.mrx.xfriendship.model.dto.*;
import com.mrx.xfriendship.model.po.UserPO;
import com.mrx.xfriendship.model.vo.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;

import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import static com.mrx.xfriendship.service.ISystemInfoService.SITE_PV_KEY;

/**
 * @author Mr.X
 * @since 2023-01-21 12:52
 */
@XLogService
public class AdminServiceImpl implements IAdminService {

    @Resource
    private DashboardMapper dashboardMapper;

    @Resource
    private IRedisService redisService;

    @Resource
    private IUserService userService;

    @Resource
    private ITagService tagService;

    @Resource
    private IPostService postService;

    @Resource
    private ISystemInfoService systemInfoService;

    private static final Logger logger = LoggerFactory.getLogger(AdminServiceImpl.class);

    @Override
    public DashboardDTO dashboard() {
        LocalDateTime now = LocalDateTime.now();
        int fromDay = now.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)).plusDays(1).getDayOfMonth();
        // between and 是 左闭右开区间
        int nowDay = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).minusDays(1).getDayOfMonth() + 1;

        DashboardDTO dto = new DashboardDTO();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
        Collector<UserPO, ?, Map<String, Integer>> toMap = Collectors.toMap(it -> formatter.format(it.getCreateTime()), it -> 1, Integer::sum);

        dto.setUserIncrement(dashboardMapper.selectUserIncrement(fromDay, nowDay).stream().collect(toMap));
        dto.setCommentIncrement(dashboardMapper.selectCommentIncrement(fromDay, nowDay).stream().collect(toMap));
        dto.setPostIncrement(dashboardMapper.selectPostIncrement(fromDay, nowDay).stream().collect(toMap));

        dto.setUserGenderCount(dashboardMapper.userGenderCount().stream().collect(pairCollector(key -> "1".equals(key) ? "男" : "女")));
        dto.setUserAreaCount(dashboardMapper.userAreaCount().stream().collect(pairCollector()));
        // 先查 redis 中是否还存在尚未同步到数据库的数据, 如果有, 那就将其合并一下并展示出来, 如果没有, 那就只展示 数据库数据
        JSONObject json = Optional.ofNullable(redisService.getSitePvList(false)).map(this::mergeSitePV)
                .orElse(systemInfoService.getSystemInfoByKey(SITE_PV_KEY).getJSONValue());
        dto.setGuestAreaCount(json.toJavaObject(new TypeReference<Map<String, Integer>>() {
        }));
        return dto;
    }

    @Override
    public PageDTO<UserManagerDTO> listUsers(UserManagerPageVO vo) {
        return userService.listUsers(vo);
    }

    @Override
    public void updateUser(UpdateUserVO vo) {
        userService.updateUser(vo);
    }

    @Override
    public void deleteUser(Integer id) {
        userService.deleteUser(id);
    }

    @Override
    public PageDTO<TagDTO> listTags(TagManagerPageVO vo) {
        return tagService.listTags(vo);
    }

    @Override
    public void updateTag(TagDTO tagDTO) {
        tagService.updateTag(tagDTO);
    }

    @Override
    public void addTag(TagDTO dto) {
        tagService.addTag(dto);
    }

    @Override
    public PageDTO<PostDTO> listPosts(PostManagePageVO vo) {
        return postService.listPosts(vo);
    }

    @Override
    public void updatePost(PostDTO dto) {
        postService.updatePost(dto);
    }

    @Override
    public void deletePost(Integer id) {
        postService.delete(id);
    }

    @Override
    public void deleteTag(Integer id) {
        tagService.delete(id);
    }

    @Override
    public PageDTO<SystemInfoDTO> listConfig(SystemManagerVO vo) {
        // 暂时不考虑分页, 为了兼容前端设置的 假分页
        List<SystemInfoDTO> data = systemInfoService.listSystemInfo(vo);
        return PageDTO.newPage(vo, data, data.size());
    }

    @Override
    public void updateConfig(UpdateSystemInfoVO vo) {
        // TODO: 2023-03-19 上午 9:55:50 参数校验
        systemInfoService.updateSystemInfo(vo);
    }

    private Collector<PairDTO, ?, Map<String, Integer>> pairCollector(@Nullable Function<String, String> keyTranslator) {
        final Function<String, String> translator = keyTranslator == null ? Function.identity() : keyTranslator;
        return Collectors.toMap(it -> translator.apply(it.getFirst()), PairDTO::getSecond);
    }

    private Collector<PairDTO, ?, Map<String, Integer>> pairCollector() {
        return pairCollector(null);
    }

    /**
     * 合并 redis 和 数据库中的 sitePV 差异
     *
     * @param json redis 中的数据
     * @return 合并之后的数据
     */
    private JSONObject mergeSitePV(JSONObject json) {
        logger.debug("before mergeSitePV: {}", json);
        Optional.ofNullable(systemInfoService.getSystemInfoByKey(SITE_PV_KEY))
                .map(value -> JSON.parseObject(value.getValue()))
                .ifPresent(it -> it.forEach((k, v) -> json.merge(k, v, (o, n) -> Integer.parseInt(o + "") + Integer.parseInt(n + ""))));
        logger.debug("after mergeSitePV: {}", json);
        return json;
    }

}
